package com.masiyi.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description ioc容器
 * @Author masiyi
 * @Date 2023/1/14
 **/
public class ApplicationContext {

    //一级缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    //拿到包路径
    ClassLoader classLoader = ApplicationContext.class.getClassLoader();

    //存储创建bean的中间类集合
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //存储BeanPostProcessor的list
    private final List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    /**
     * 构造器
     *
     * @param configClass
     */
    public ApplicationContext(Class configClass) {
        //查看是否有ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String[] paths = componentScanAnnotation.value();
            for (String path : paths) {
                URL resource = classLoader.getResource(path.replace(".", "/"));
                //拿到该路径下所有的class文件
                File file = new File(resource.getFile());
                if (file.isDirectory()) {
                    File[] files = file.listFiles();
                    for (File f : files) {
                        try {
                            String filePath = f.getPath();
                            //拿到com.masiyi.service.MySpringConfig
                            String sub = filePath.substring(filePath.indexOf("com"), filePath.indexOf(".class"));
                            String classes = sub.replace("\\", ".");
                            Class<?> aClass = classLoader.loadClass(classes);
                            //创建beanDefinition
                            String beanName = aClass.getName().substring(aClass.getName().lastIndexOf(".") + 1);
                            createBean(beanName, aClass);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        //将bean实例化到一级缓存中
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //单例
            if ("singleton".equals(beanDefinition.getScope())) {
                try {
                    if (!singletonObjects.containsKey(beanName)) {
                        Class aClass = beanDefinition.getaClass();
                        Object bean = aClass.getDeclaredConstructor().newInstance();
                        //实现BeanPostProcessor逻辑
                        populateBean(bean, aClass);
                        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                            bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
                            bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
                        }
                        //属性填充
                        singletonObjects.put(beanName, bean);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 属性填充
     *
     * @param bean
     * @param aClass
     */
    private void populateBean(Object bean, Class aClass) {
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                declaredField.setAccessible(true);
                try {
                    declaredField.set(bean, getBean(declaredField.getName()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建Bean
     *
     * @param beanName
     * @param aClass
     */
    private void createBean(String beanName, Class<?> aClass) {
        //注册有Component注解的bean
        if (aClass.isAnnotationPresent(Component.class)) {

            Component component = aClass.getAnnotation(Component.class);
            BeanDefinition beanDefinition = new BeanDefinition();
            if (aClass.isAnnotationPresent(Scope.class)) {
                Scope scope = aClass.getAnnotation(Scope.class);
                beanDefinition.setScope(scope.value());
            } else {
                beanDefinition.setScope("singleton");
            }
            beanDefinition.setaClass(aClass);

            String value = component.value();
            if ("".equals(value)) {
                beanDefinitionMap.put(beanName, beanDefinition);
            } else {
                beanDefinitionMap.put(value, beanDefinition);
            }
            //实现BeanPostProcessor逻辑
            if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                BeanPostProcessor beanPostProcessor = null;
                try {
                    beanPostProcessor = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                    beanPostProcessorList.add(beanPostProcessor);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取bean
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException();
        }
        try {
            Class aClass = beanDefinition.getaClass();
            if ("singleton".equals(beanDefinition.getScope())) {
                //如果是单例，直接返回缓存里的bean
                Object bean = this.singletonObjects.get(beanName);
                if (bean == null) {
                    Object singletonBean = aClass.getDeclaredConstructor().newInstance();
                    this.singletonObjects.put(beanName, singletonBean);
                    return singletonBean;
                }
                return bean;
            } else {
                //如果是多例，直接返回新的bean
                return aClass.getDeclaredConstructor().newInstance();

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
